/*
 * @Descripttion:
 * @version: 1.0.0
 * @Date: 2021-04-08 16:26:10
 * @LastEditTime: 2022-06-15 10:47:39
 */
import {
  Viewer,
  defined,
  EasingFunction,
  BoundingSphere,
  Cartesian3,
  Math,
  HeadingPitchRange,
  Entity,
  EntityCollection,
  DataSource,
  ImageryLayer,
  Cesium3DTileset,
  TimeDynamicPointCloud,
  Rectangle,
  Matrix4,
  Camera,
} from "cesium";
import { IRoamPath, MouseHandler, RoamType, MathUtils } from "../CustomCesium";

//漫游对象
export class RoamPath implements IRoamPath {
  /**
   * The Code component.
   */
  code: string;
  /**
   * The Path component.
   */
  path: [RoamType, Cartesian3, Cartesian3, number, HeadingPitchRange, number];
  /**
   * The Count component.
   */
  count: number;
  constructor(
    code: string,
    path: [RoamType, Cartesian3, Cartesian3, number, HeadingPitchRange, number],
    count: number
  ) {
    this.code = code;
    this.path = path;
    this.count = count;
  }
}

/**
 * @descripttion: 系统漫游
 */
class CameraRoam {
  private viewer: Viewer;
  pathManager: PathManager;
  private isStarted;
  private isPaused;
  //当前漫游
  private currentRoam: object;

  /**
   * @descripttion: 系统漫游
   * @param {*} viewer cesium视图
   */
  constructor(viewer: Viewer) {
    this.viewer = this.init(viewer);
    this.pathManager = new PathManager();
    this.isStarted = false;
    this.isPaused = false;
    this.currentRoam = {
      roam: undefined,
      index: -1,
    };
  }

  /**
   * @descripttion: 初始化
   * @param {Viewer} viewer 视图
   * @return {Viewer} 视图
   */
  private init(viewer: Viewer) {
    if (!defined(viewer)) throw new Error("EventListener,无效的视图对象！");
    return viewer;
  }

  /**
   * @descripttion: 相机飞行到包围盒
   * @param {BoundingSphere} boundingSphere 包围盒
   * @param {object} options 参数
   * @return {*}
   */
  flyToBoundingSphere(
    boundingSphere: BoundingSphere,
    options: {
      offset?: HeadingPitchRange;
      complete?: Camera.FlightCompleteCallback;
      cancel?: Camera.FlightCancelledCallback;
      endTransform?: Matrix4;
      maximumHeight?: number;
      pitchAdjustHeight?: number;
      flyOverLongitude?: number;
      flyOverLongitudeWeight?: number;
    }
  ) {
    this.isRoaming() && this.exitRoam();
    this.viewer.camera.flyToBoundingSphere(boundingSphere, {
      duration: 8,
      easingFunction: EasingFunction.EXPONENTIAL_IN_OUT,
      ...options,
    });
  }

  /**
   * @descripttion: 相机飞行
   * @param {object} options 参数
   * @return {void}
   */
  flyTo(options: {
    destination: Cartesian3 | Rectangle;
    orientation?: any;
    duration?: number;
    complete?: Camera.FlightCompleteCallback;
    cancel?: Camera.FlightCancelledCallback;
    endTransform?: Matrix4;
    maximumHeight?: number;
    pitchAdjustHeight?: number;
    flyOverLongitude?: number;
    flyOverLongitudeWeight?: number;
    convert?: boolean;
    easingFunction?: EasingFunction.Callback;
  }) {
    this.isRoaming() && this.exitRoam();
    return this.viewer.camera.flyTo({ ...options });
  }

  /**
   * @descripttion: 根据目标飞行
   * @param {*}
   * @return {*}
   */
  flyToTarget(
    target:
      | Entity
      | Entity[]
      | EntityCollection
      | DataSource
      | ImageryLayer
      | Cesium3DTileset
      | TimeDynamicPointCloud
      | Promise<
          | Entity
          | Entity[]
          | EntityCollection
          | DataSource
          | ImageryLayer
          | Cesium3DTileset
          | TimeDynamicPointCloud
        >,
    options?: {
      duration?: number;
      maximumHeight?: number;
      offset?: HeadingPitchRange;
    }
  ) {
    this.isRoaming() && this.exitRoam();
    return this.viewer.flyTo(target, { ...options });
  }

  /**
   * @descripttion: 初始化漫游关键帧
   * @param {RoamType} roamType 漫游类型
   * @param {Cartesian3} position 相机位置
   * @param {Cartesian3} viewPosition 视点
   * @param {number} rotation 旋转角度
   * @param {HeadingPitchRange} headingPitchRange 相机姿态
   * @param {number} time 变化时间
   * @return {*} path
   */
  initRoamFrame(
    roamType: RoamType = RoamType.FLYTO,
    position: Cartesian3 = Cartesian3.ZERO,
    viewPosition: Cartesian3 = Cartesian3.ZERO,
    rotation = 0,
    headingPitchRange: HeadingPitchRange = new HeadingPitchRange(),
    time = 5
  ): [RoamType, Cartesian3, Cartesian3, number, HeadingPitchRange, number] {
    return [
      roamType,
      position,
      viewPosition,
      rotation,
      headingPitchRange,
      time,
    ];
  }

  /**
   * @descripttion: 执行漫游
   * @param {string} code 唯一索引
   * @param {MouseHandler} manager 事件监听管理器
   * @param {function} callback 回调
   * @return {*}
   */
  applyRoam(
    code: string,
    manager: MouseHandler,
    callback: (res: boolean) => void
  ) {
    //判断是否正在漫游
    if (this.isStarted) {
      callback && callback(false);
      console.warn("正在漫游!");
      return;
    }
    //获取当前漫游
    const roam = this.pathManager.get(code);
    //无此漫游
    if (!roam) {
      callback && callback(false);
      return;
    }
    const { path, count } = roam;
    //判断是否有漫游帧
    if (count <= 0) {
      callback && callback(false);
      return;
    }
    this.isStarted = true;
    /**
     * @descripttion: canvas点击方法
     * @return {void}
     */
    const canvasClick = () => {
      this.exitRoam();
      // 删除事件绑定
      // this.viewer.scene.canvas.removeEventListener('click', canvasClick, false);
      manager.destroyHandlerByKey("roamListener");
    };
    //漫游过程中，为鼠标操作添加监听
    // this.viewer.scene.canvas.addEventListener('click', canvasClick, false);
    manager.addMouseClickHandler("roamListener", canvasClick);
    this.applyRoamFrame(path, 0, (res: any) => {
      this.isStarted = false;
      callback && callback(res);
    });
  }

  /**
   * @descripttion: 暂停当前漫游
   * @return {void}
   */
  pauseRoam() {
    if (this.isStarted) {
      this.viewer.camera.cancelFlight();
      this.isPaused = true;
    }
  }

  /**
   * @descripttion: 退出当前漫游
   * @return {void}
   */
  exitRoam() {
    if (this.isStarted) {
      this.viewer.camera.cancelFlight();
      this.isStarted = false;
      this.isPaused = false;
      this.clearRoamInfo();
    }
  }

  /**
   * @descripttion: 获取当前漫游状态
   * @return {boolean} 状态
   */
  isRoaming = () => this.isStarted;

  /**
   * @descripttion: 执行漫游帧
   * @param {Array} path 漫游路径
   * @param {number} index 漫游索引
   * @param {function} callback 回调
   * @return {Promise} 执行结果
   */
  private applyRoamFrame(
    path: Array<any>,
    index: number,
    callback: (res: boolean) => void
  ) {
    if (!this.isStarted) {
      callback(false);
      return;
    }
    const roamFrame = path[index];
    const frameCount = path.length;
    this.currentRoam = {
      roam: roamFrame,
      index,
    };
    if (frameCount === index) {
      callback(true);
      return;
    }
    if (roamFrame) {
      const roamType = roamFrame[0],
        position = roamFrame[1],
        viewPosition = roamFrame[2],
        rotation = roamFrame[3],
        headingPitchRange = roamFrame[4],
        time = roamFrame[5];
      this.flyRoam(position, headingPitchRange, index === 0 ? NaN : time).then(
        (res) => {
          if (res) {
            //飞行漫游||平移漫游
            roamType !== 2 || rotation === 0
              ? this.applyRoamFrame(path, index + 1, callback)
              : //旋转漫游
                this.rotateRoam(viewPosition, rotation).then(() =>
                  this.applyRoamFrame(path, index + 1, callback)
                );
          } else callback(false);
        }
      );
    } else
      frameCount !== index &&
        console.log(new Error(`漫游参数错误！path:${path}index:${index}`));
  }

  /**
   * @descripttion: 飞行
   * @param {Cartesian3} position 终点位置
   * @param {HeadingPitchRange} headingPitchRange 相机姿态
   * @param {number} time 持续时间
   * @return {void}
   */
  private flyRoam = (
    position: Cartesian3,
    headingPitchRange: HeadingPitchRange,
    time: number
  ) =>
    new Promise((resolve) => {
      //获取相机位置
      const currentPosition = { ...this.viewer.camera.position };
      //创建新对象
      const cameraPosition = new Cartesian3(
        currentPosition.x,
        currentPosition.y,
        currentPosition.z
      );
      //目标位置与当前位置的距离
      const distance = Cartesian3.distance(position, cameraPosition);
      if (distance > 1) {
        // const boundingSphere = new Cesium.BoundingSphere(position);
        //转为地理坐标
        const gcs = MathUtils.car3ToGCS(this.viewer, position);
        //相机姿态
        const hpr = { ...headingPitchRange };
        //最大相机飞行高度
        const maxHeightOption =
          distance < 500 ? { maximumHeight: gcs.height || 1000 } : {};
        //飞行参数
        const options = !Number.isNaN(time)
          ? {
              destination: position,
              // offset: headingPitchRange,
              orientation: hpr,
              duration: time,
              easingFunction: EasingFunction.LINEAR_NONE,
              complete: () => resolve(true),
              cancel: () => resolve(false),
              ...maxHeightOption,
            }
          : {
              destination: position,
              // offset: new Cesium.HeadingPitchRange(0, 0, 0),
              // pitchAdjustHeight: 180,
              orientation: hpr,
              duration: 8,
              easingFunction: EasingFunction.EXPONENTIAL_IN_OUT,
              complete: () => resolve(true),
              cancel: () => resolve(false),
            };
        // this.viewer.camera.flyToBoundingSphere(boundingSphere, { ...options });
        this.viewer.camera.flyTo({ ...options });
      } else resolve(true);
    }).catch((err) => console.log(new Error(err.toString())));

  /**
   * @descripttion: 绕点旋转
   * @param {*} viewPosition 视点
   * @param {*} rotation 旋转角度
   * @return {Promise} 结果
   */
  private rotateRoam = async (viewPosition: Cartesian3, rotation: number) => {
    let index = 1;
    const result = await new Promise((resolve) => {
      const timer = setInterval(() => {
        //是否暂停
        if (this.isPaused) return;
        //是否停止
        if (!this.isStarted) {
          clearInterval(timer);
          resolve(false);
          return;
        }
        this.viewer.camera.rotate(viewPosition, -Math.toRadians(0.2));
        index += 0.2;
        if (index > rotation) {
          clearInterval(timer);
          resolve(true);
        }
      }, 10);
    });
    return result;
  };

  /**
   * @descripttion:清除漫游信息
   * @return {void}
   */
  clearRoamInfo() {
    this.currentRoam = {
      roam: undefined,
      index: -1,
    };
  }
}

/**
 * @descripttion: 路径管理器
 */
class PathManager {
  private roamPaths: IRoamPath[];

  /**
   * @descripttion: 路径管理器
   */
  constructor() {
    this.roamPaths = [];
  }

  /**
   * @descripttion: 查找漫游路径（根据code）
   * @param {string} code 唯一标识
   * @return {object} 漫游对象
   */
  get(code: string) {
    const index = this.isExists(code);
    return index !== -1 ? this.roamPaths[index] : undefined;
  }

  /**
   * @descripttion: 获取所有漫游对象
   * @return {Array} 所有漫游对象
   */
  getAll() {
    return this.roamPaths;
  }

  /**
   * @descripttion: 添加漫游对象
   * @param {string} code 唯一标识
   * @param {*} path 漫游路径
   * @return {*} 结果
   */
  add(
    code: string,
    path: [RoamType, Cartesian3, Cartesian3, number, HeadingPitchRange, number]
  ) {
    let result = false;
    if (this.isExists(code) === -1 && Array.isArray(path)) {
      this.roamPaths.push({ code, path, count: path.length });
      result = true;
    }
    return result;
  }

  /**
   * @descripttion: 是否存在
   * @param {string} code 唯一标识
   * @return {boolean} 存在返回索引，不存在undefined
   */
  isExists(code = "") {
    return this.roamPaths.findIndex((item) => item.code === code);
  }

  /**
   * @descripttion: 删除漫游对象
   * @param {string} code 唯一索引
   * @return {boolean} 结果
   */
  deleteByCode(code = "") {
    let result = false;
    if (this.isExists(code)) {
      const newPaths = this.roamPaths.filter((item) => item.code !== code);
      this.roamPaths = [...newPaths];
      result = true;
    }
    return result;
  }

  /**
   * @descripttion: 删除所有漫游对象
   * @return {void}
   */
  deleteAll() {
    this.roamPaths = [];
  }
}

export default CameraRoam;
